/*
pbrt source code Copyright(c) 1998-2012 Matt Pharr and Greg Humphreys.

ATTENTION - THIS FILE CONTAINS THIRD PARTY OPEN SOURCE CODE: PBRT
IT IS CLEARED ONLY FOR LIMITED USE BY MICROSOFT RESEARCH FOR THE
TIME-OF-FLIGHT RAYTRACER PRODUCT. DO NOT USE OR SHARE THIS CODE
WITHOUT APPROVAL PURSUANT TO THE MICROSOFT OPEN SOURCE SOFTWARE
APPROVAL POLICY.

This file is part of pbrt.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "stdafx.h"
#include "renderers/tof.h"


ToFSample::ToFSample(int maxDepth) : MLTSample(maxDepth) {
}

ToFSample::ToFSample(MLTSample &orig) : MLTSample(orig) {
}


ToFRenderData::ToFRenderData(MemoryArena &arr, RNG &rand, PathVertex *cp,
	PathVertex *lp, float *cpd, float *lpd, int cpl, int lpl) :
	MLTRenderData(arr, rand, cp, lp, cpl, lpl),
	cameraPathDistances(cpd), lightPathDistances(lpd) {
}


ToFRenderer::ToFRenderer(int pps, int nBoot, float lsp,
	int maxReject, int depth, Camera *cam, bool doBi, bool gt) :
	MetropolisRenderer(pps, nBoot, pps, lsp, true,
	maxReject, depth, cam, doBi) {
	groundTruth = gt;
}


ToFRenderer *CreateToFRenderer(const ParamSet &params, Camera *camera) {
	float largeStepProbability = params.FindOneFloat(
		"largestepprobability", .25f);
	int perPixelSamples = params.FindOneInt("samplesperpixel", 128);
	int nBootstrap = params.FindOneInt("bootstrapsamples", 100000);
	int maxRejects = params.FindOneInt("maxconsecutiverejects", 512);
	int maxDepth = params.FindOneInt("maxdepth", 7);
	bool doBidirectional = params.FindOneBool("bidirectional", true);
	bool groundtruth = params.FindOneBool("groundtruth", false);

	if (PbrtOptions.quickRender) {
		perPixelSamples = max(1, perPixelSamples / 4);
		nBootstrap = max(1, nBootstrap / 4);
	}

	return new ToFRenderer(perPixelSamples, nBootstrap, largeStepProbability,
		maxRejects, maxDepth, camera, doBidirectional, groundtruth);
}


void ToFRenderer::Render(const Scene *scene) {
	this->scene = scene;


	if (scene->lights.size() > 0) {

		lightDistribution = ComputeLightSamplingCDF(scene);
		vector<PathVertex> cameraPath(maxDepth, PathVertex());
		vector<PathVertex> lightPath(maxDepth, PathVertex());
		vector<float> cameraPathDistances(maxDepth);
		vector<float> lightPathDistances(maxDepth);
		MemoryArena arena;
		RNG rng(0);
		ToFRenderData data(arena, rng, &cameraPath[0], &lightPath[0],
			&cameraPathDistances[0], &lightPathDistances[0]);

		WriteHeader();
		if (groundTruth && nDirectPixelSamples > 0) {
			LaunchDirectTasks(data);
		}
		else {
			LaunchMLT(data);
		}
		camera->film->WriteImage();
	}

}


void ToFRenderer::LaunchDirectTasks(ToFRenderData &data) {
	int x0, x1, y0, y1;
	float t0 = camera->shutterOpen, t1 = camera->shutterClose;
	camera->film->GetPixelExtent(&x0, &x1, &y0, &y1);

	LDSampler sampler(x0, x1, y0, y1, nDirectPixelSamples, t0, t1);
	Sample *sample = new Sample(&sampler, directLightingIntegrator, NULL, scene);

	vector<Task *> directTasks;
	int nDirectTasks = max(32 * NumSystemCores(),
		(camera->film->xResolution * camera->film->yResolution) / (16 * 16));
	nDirectTasks = RoundUpPow2(nDirectTasks);

	ProgressReporter directProgress(nDirectTasks, "Ground Truth");
	for (int i = 0; i < nDirectTasks; ++i)
		directTasks.push_back(new ToFDirectTask(scene, this, camera,
		directProgress, &sampler, sample, i, nDirectTasks));
	reverse(directTasks.begin(), directTasks.end());
	EnqueueTasks(directTasks);
	WaitForAllTasks();
	for (uint32_t i = 0; i < directTasks.size(); ++i)
		delete directTasks[i];
	delete sample;
	directProgress.Done();
}


void ToFRenderer::LaunchMLT(ToFRenderData &data) {
	// Take initial set of samples to compute $b$
	float sumI = 0.f;
	vector<float> bootstrapI;
	float b = Bootstrap(data, &sumI, &bootstrapI);

	// Select initial sample from bootstrap samples
	ToFSample initialSample =
		ToFSample(GetInitialSample(data, sumI, &bootstrapI[0]));

	// Launch tasks to generate Metropolis samples
	LaunchMLTTasks(data, initialSample, b);
	delete lightDistribution;
}


void ToFRenderer::LaunchMLTTasks(ToFRenderData &data,
	ToFSample &initialSample, float b) {
	float t0 = camera->shutterOpen, t1 = camera->shutterClose;
	int x0, x1, y0, y1;
	camera->film->GetPixelExtent(&x0, &x1, &y0, &y1);

	uint32_t nTasks = largeStepsPerPixel;
	uint32_t largeStepRate = nPixelSamples / largeStepsPerPixel;
	Info("MLT running %d tasks, large step rate %d", nTasks, largeStepRate);
	Assert(IsPowerOf2(nTasks));

	uint32_t scramble[2] = { data.rng.RandomUInt(), data.rng.RandomUInt() };
	uint32_t pfreq = (x1 - x0) * (y1 - y0);

	ProgressReporter progress(nTasks * largeStepRate, "Backscatter");
	vector<Task *> tasks;
	for (uint32_t i = 0; i < nTasks; ++i) {
		float d[2];
		Sample02(i, scramble, d);
		tasks.push_back(new ToFTask(progress, pfreq, i,
			d[0], d[1], x0, x1, y0, y1, t0, t1, b, initialSample, this));
	}
	EnqueueTasks(tasks);
	WaitForAllTasks();
	for (uint32_t i = 0; i < tasks.size(); ++i)
		delete tasks[i];
	progress.Done();
}


Spectrum ToFRenderer::PathL(ToFRenderData &data, ToFSample &sample) const {
	if (!InSampleBounds(sample)) return 0.f;

	RayDifferential cameraRay;
	float cameraWt = GenerateCameraRay(sample, &cameraRay);
	data.cameraPathLength = GeneratePath(data, sample, cameraRay, cameraWt, false);
	sample.histogram.clear();

	if (!bidirectional) {
		return Lpath(data, sample);
	}
	else {
		float lightRayPdf;
		Ray lightRay;
		Spectrum lightWt;

		lightWt = SampleLightBidir(data, sample, &lightRayPdf, &lightRay);
		if (lightWt.IsBlack() || lightRayPdf == 0.f) {
			return Lpath(data, sample);
		}
		else {
			RayDifferential ray(lightRay);
			data.lightPathLength = GeneratePath(data, sample, ray, lightWt, true);
			return Lbidir(data, sample);
		}
	}
}


bool ToFRenderer::InSampleBounds(ToFSample &sample) const {
	int x = static_cast<int>(sample.cameraSample.imageX);
	int y = static_cast<int>(sample.cameraSample.imageY);

	int xmin, xmax, ymin, ymax;
	camera->film->GetSampleExtent(&xmin, &xmax, &ymin, &ymax);

	return (x >= xmin && x <= xmax && y >= ymin && y <= ymax);
}


Spectrum ToFRenderer::Lpath(ToFRenderData &data, ToFSample &sample) const {
	Spectrum L(0.f);
	bool prevSpecular = true;
	for (int i = 0; i < data.cameraPathLength; ++i) {
		// Initialize basic variables for camera path vertex
		const PathVertex &vc = data.cameraPath[i];
		const Point &pc = vc.bsdf->dgShading.p;
		const Normal &nc = vc.bsdf->dgShading.nn;
		Spectrum localL(0.f);

		// Add emitted light from vertex if appropriate
		if (prevSpecular)
			localL += vc.alpha * vc.isect.Le(vc.wPrev);

		// Add direct illumination for Metropolis path vertex
		float light2isect = 0.f;
		localL += DirectLightingVertex(data, sample, i, vc, pc, nc, &light2isect);

		// Save relevant ToF bounce data
		SaveToFData(sample, localL, data.cameraPathDistances[i] + light2isect);

		prevSpecular = vc.specularBounce;
		L += localL;
	}

	// Note: Infinite Light Sources - Light Maps Unsupported
	// Normally we would add the contribution of the escaped ray if it existed.
	// This contribution would be calculated by sampling the point and direction 
	// on a radiance map. However, measuring pathlengths to a radiance map does
	// not make logical sense and so we will omit this contribution.

	return L;
}


Spectrum ToFRenderer::DirectLightingVertex(ToFRenderData &data,
	ToFSample &sample, int pathIdx, const PathVertex &vc, const Point &pc,
	const Normal &nc, float *distance) const {
	Spectrum Ld(0.f);

	// Choose light and call _EstimateDirect()_ for Metropolis vertex
	const LightingSample &ls = sample.lightingSamples[pathIdx];
	float lightPdf;
	uint32_t lightNum = lightDistribution->SampleDiscrete(ls.lightNum,
		&lightPdf);
	const Light *light = scene->lights[lightNum];

	PBRT_MLT_STARTED_ESTIMATE_DIRECT();
	Ld = vc.alpha *
		EstimateDirect(scene, this, data.arena, light, pc, nc, vc.wPrev,
		vc.isect.rayEpsilon, sample.cameraSample.time, vc.bsdf, data.rng,
		ls.lightSample, ls.bsdfSample,
		BxDFType(BSDF_ALL & ~BSDF_SPECULAR)) / lightPdf;
	PBRT_MLT_FINISHED_ESTIMATE_DIRECT();

	// Extract distance from point to light for ToF histogram
	Normal normal;
	Ray lightRay;
	light->Sample_L(scene, ls.lightSample, data.rng.RandomFloat(),
		data.rng.RandomFloat(), sample.cameraSample.time, &lightRay,
		&normal, &lightPdf);
	Point pl = lightRay.o;
	*distance = Distance(pl, pc);

	return Ld;
}


void ToFRenderer::SaveToFData(ToFSample &sample, Spectrum L, float distance) const
{
	float radiance = L.Average(camera->lambdaMin, camera->lambdaMax);
	HistogramSample histSample(distance, radiance, sample.cameraSample);
	sample.histogram.push_back(histSample);
}


Spectrum ToFRenderer::Lbidir(ToFRenderData &data, ToFSample &sample) const {
	Spectrum L(0.f);
	bool prevSpecular = true, allSpecular = true;

	// Compute number of specular vertices for each path length
	int nVerts = data.cameraPathLength + data.lightPathLength + 2;
	int *nSpecularVertices = new int[nVerts];
	GetNumSpecularVertices(data, nSpecularVertices);

	for (int i = 0; i < data.cameraPathLength; ++i) {
		// Initialize basic variables for camera path vertex
		Spectrum localL(0.f);
		const PathVertex &vc = data.cameraPath[i];
		const Point &pc = vc.bsdf->dgShading.p;
		const Normal &nc = vc.bsdf->dgShading.nn;

		// Add emitted light from vertex if appropriate
		if (prevSpecular)
			localL += vc.alpha * vc.isect.Le(vc.wPrev);

		// Compute direct illumination for Metropolis path vertex
		Spectrum Ld(0.f);
		float light2bounce = 0.f;
		Ld = DirectLightingVertex(data, sample, i, vc, pc, nc, &light2bounce);

		prevSpecular = vc.specularBounce;
		localL += Ld / (i + 1 - nSpecularVertices[i + 1]);

		// Save relevant ToF bounce data for first bounce
		SaveToFData(sample, localL, data.cameraPathDistances[i] + light2bounce);

		if (!vc.specularBounce) {
			// Loop over light path vertices and connect to camera vertex
			for (int j = 0; j < data.lightPathLength; ++j) {
				float light2camera = 0.f;
				Spectrum lightLocalL = LightPathL(data, sample, pc, vc, nc,
					nSpecularVertices, i, j, &light2camera);
				localL += lightLocalL;

				// Save relevant ToF bounce data
				SaveToFData(sample, lightLocalL, data.cameraPathDistances[i] +
					data.lightPathDistances[j] + light2camera);
			}
		}
		L += localL;
	}

	// Note: Infinite Light Sources - Light Maps Unsupported
	// Normally we would add the contribution of the escaped ray if it existed.
	// This contribution would be calculated by sampling the point and direction 
	// on a radiance map. However, measuring pathlengths to a radiance map does
	// not make logical sense and so we will omit this contribution.

	delete[] nSpecularVertices;
	return L;
}


Spectrum ToFRenderer::LightPathL(MLTRenderData &data, MLTSample &sample,
	const Point &pc, const PathVertex &vc, const Normal &nc,
	int *nSpecularVertices, int cameraPathIdx, int lightPathIdx,
	float *distance) const {

	const PathVertex &vl = data.lightPath[lightPathIdx];
	const Point &pl = vl.bsdf->dgShading.p;
	const Normal &nl = vl.bsdf->dgShading.nn;
	Spectrum L(0.f);

	if (!vl.specularBounce) {
		// Compute contribution between camera and light vertices
		Vector w = Normalize(pl - pc);
		Spectrum fc = vc.bsdf->f(vc.wPrev, w) * (1 + vc.nSpecularComponents);
		Spectrum fl = vl.bsdf->f(-w, vl.wPrev) * (1 + vl.nSpecularComponents);
		Ray r(pc, pl - pc, 1e-3f, .999f, sample.cameraSample.time);

		if (!fc.IsBlack() && !fl.IsBlack() && !scene->IntersectP(r)) {
			// Compute weight for bidirectional path, _pathWt_
			int pathIdx = cameraPathIdx + lightPathIdx + 2;
			float pathWt = 1.f / (pathIdx - nSpecularVertices[pathIdx]);
			float G = AbsDot(nc, w) * AbsDot(nl, w) / DistanceSquared(pl, pc);
			L += (vc.alpha * fc * G * fl * vl.alpha) * pathWt;

			*distance = Distance(pl, pc);
		}
	}
	return L;
}


uint32_t ToFRenderer::GeneratePath(ToFRenderData &data, ToFSample &sample,
	const RayDifferential &r, const Spectrum &a, bool isLightPath) const {
	RayDifferential ray = r;
	Spectrum alpha = a;

	vector<PathSample> *samples;
	float *distances;
	PathVertex *path = isLightPath ? data.lightPath : data.cameraPath;
	distances = isLightPath ? data.lightPathDistances : data.cameraPathDistances;
	samples = isLightPath ? &sample.lightPathSamples : &sample.cameraPathSamples;

	if (!isLightPath) data.escapedAlpha = 0.f;

	uint32_t length = 0;
	for (; length < samples->size(); ++length) {
		// Try to generate next vertex of ray path
		PathVertex *v = &path[length];
		bool hitScene = GeneratePathVertex(data, ray, alpha, length, isLightPath,
			v, distances);

		if (!hitScene) break;

		// Sample direction for outgoing Metropolis path direction
		float pdf;
		BxDFType flags;
		Spectrum f = v->bsdf->Sample_f(-ray.d, &v->wNext,
			(*samples)[length].bsdfSample, &pdf, BSDF_ALL, &flags);
		v->specularBounce = (flags & BSDF_SPECULAR) != 0;
		v->nSpecularComponents = v->bsdf->NumComponents(BxDFType(BSDF_SPECULAR |
			BSDF_REFLECTION | BSDF_TRANSMISSION));
		if (f.IsBlack() || pdf == 0.f ||
			!RussianRoulette(*v, f, pdf, (*samples)[length].rrSample, &ray, &alpha))
		{
			return length + 1;
		}
	}
	return length;
}


bool ToFRenderer::GeneratePathVertex(ToFRenderData &data, RayDifferential &ray,
	Spectrum &alpha, int vertexIdx, bool isLightPath, PathVertex *v,
	float *distances) const {
	if (!scene->Intersect(ray, &v->isect)) {
		// Handle ray that leaves the scene during path generation
		if (!isLightPath) {
			data.escapedAlpha = alpha;
			data.escapedRay = ray;
		}
		distances[vertexIdx] = INFINITY;
		return false;
	}

	// Record information for current path vertex
	v->alpha = alpha;
	BSDF *bsdf = v->isect.GetBSDF(ray, data.arena);
	v->bsdf = bsdf;
	v->wPrev = -ray.d;

	distances[vertexIdx] = Distance(ray.o, bsdf->dgShading.p);
	if (vertexIdx > 0) distances[vertexIdx] += distances[vertexIdx - 1];

	return true;
}


void ToFRenderer::WriteHeader() {
	FILE* fp = fopen(camera->film->filename.c_str(), "w");
	if (!fp) {
		Severe("Film file %s could not be opened", camera->film->filename);
	}
	camera->Print(fp);
	fclose(fp);
}


ToFTask::ToFTask(ProgressReporter &prog, uint32_t pfreq, uint32_t tn,
	float ddx, float ddy, int xx0, int xx1, int yy0, int yy1, float tt0,
	float tt1, float bb, const ToFSample &is, ToFRenderer *ren) :
	MLTTask(prog, pfreq, tn, ddx, ddy, xx0, xx1, yy0, yy1, tt0, tt1, bb, is,
	ren, NULL), initialSample(is) {
	renderer = ren;
}


void ToFTask::Run() {
	// Declare variables for storing and computing MLT samples
	vector<PathVertex> cameraPath(renderer->maxDepth, PathVertex());
	vector<PathVertex> lightPath(renderer->maxDepth, PathVertex());
	MemoryArena arena;
	RNG rng(taskNum);
	float *cameraDistances = new float[renderer->maxDepth];
	float *lightDistances = new float[renderer->maxDepth];
	ToFRenderData data(arena, rng, &cameraPath[0], &lightPath[0], 
		cameraDistances, lightDistances);

	// Declare basic _MLTTask_ variables and prepare for sampling
	vector<ToFSample> samples(2, ToFSample(renderer->maxDepth));
	Spectrum L[2];
	float I[2];
	Init(data, &samples[0], L, I);

	for (uint64_t s = 0; s < nTaskSamples; ++s) {
		// Compute proposed mutation to current sample
		samples[proposed] = samples[current];
		Mutate(data, s, &samples[proposed]);

		// Compute contribution of proposed sample
		L[proposed] = renderer->PathL(data, samples[proposed]);
		I[proposed] = renderer->calcI(L[proposed]);
		data.arena.FreeAll();

		// Compute acceptance probability for proposed sample
		float acceptProb = min(1.f, I[proposed] / I[current]);

		// Splat current and proposed samples to _Film_
		SplatSample(acceptProb, &samples[0], L, I);

		// Randomly accept proposed path mutation (or not)
		VetMutation(data, acceptProb);
	}
	Assert(pixelNumOffset == nPixels);
}


// This implementation is the same as in MetropolisRenderer, but is necessary
// due to variable type casting and execution of the correct PathL method.
void ToFTask::Init(ToFRenderData &data, ToFSample *samples, Spectrum *L, float *I)
{
	PBRT_MLT_STARTED_TASK_INIT();
	nPixels = (x1 - x0) * (y1 - y0);
	nPixelSamples = renderer->nPixelSamples;
	largeStepRate = nPixelSamples / renderer->largeStepsPerPixel;
	Assert(largeStepRate > 1);
	nTaskSamples = uint64_t(nPixels) * uint64_t(largeStepRate);
	consecutiveRejects = 0;
	progressCounter = progressUpdateFrequency;

	current = 0;
	proposed = 1;

	// Compute _L[current]_ for initial sample
	samples[current] = initialSample;
	L[current] = renderer->PathL(data, samples[current]);
	I[current] = renderer->calcI(L[current]);
	data.arena.FreeAll();

	// Compute randomly permuted table of pixel indices for large steps
	pixelNumOffset = 0;
	largeStepPixelNum.reserve(nPixels);
	for (uint32_t i = 0; i < nPixels; ++i) largeStepPixelNum.push_back(i);
	Shuffle(&largeStepPixelNum[0], nPixels, 1, data.rng);
	PBRT_MLT_FINISHED_TASK_INIT();
}


void ToFTask::SplatSample(float acceptProb, ToFSample *samples, Spectrum *L,
	float *I) {
	// Splat current and proposed samples to _Film_
	if (I[current] > 0.f) {
		if (!isinf(1.f / I[current])) {
			float weight = b / (nPixelSamples * I[current]) * (1.f - acceptProb);
			int nSamples = samples[current].histogram.size();
			Spectrum contrib = weight * L[current] / nSamples;

			for (int i = 0; i < nSamples; i++) {
				HistogramSample result = samples[current].histogram[i];
				result.radiance *= weight;
				renderer->camera->film->Splat(samples[current].cameraSample, 
					PaintData(contrib, result));
			}
		}
	}
	if (I[proposed] > 0.f) {
		if (!isinf(1.f / I[proposed])) {
			float weight = b / (nPixelSamples * I[proposed]) * acceptProb;
			int nSamples = samples[proposed].histogram.size();
			Spectrum contrib = weight * L[proposed] / nSamples;

			for (int i = 0; i < nSamples; i++) {
				HistogramSample result = samples[proposed].histogram[i];
				result.radiance *= weight;
				renderer->camera->film->Splat(samples[proposed].cameraSample,
					PaintData(contrib, result));
			}
		}
	}
}


ToFDirectTask::ToFDirectTask(const Scene *sc, Renderer *ren, Camera *c,
	ProgressReporter &pr, Sampler *ms, Sample *sam, int tn, int tc) :
	SamplerRendererTask(sc, ren, c, pr, ms, sam, false, tn, tc) {
}

void ToFDirectTask::Run() {
	// Get sub-_Sampler_ for _SamplerRendererTask_
	sampler = mainSampler->GetSubSampler(taskNum, taskCount);
	if (!sampler) {
		reporter.Update();
		return;
	}

	// Declare local variables used for rendering loop
	MemoryArena arena;
	RNG rng(taskNum);

	// Allocate space for samples and intersections
	int maxSamples = sampler->MaximumSampleCount();
	samples = origSample->Duplicate(maxSamples);
	rays = new RayDifferential[maxSamples];
	Ls = new Spectrum[maxSamples];
	Ts = new Spectrum[maxSamples];
	isects = new Intersection[maxSamples];

	// Get samples from _Sampler_ and update image
	int sampleCount;
	while ((sampleCount = sampler->GetMoreSamples(samples, rng)) > 0) {
		// Generate camera rays and compute radiance along rays
		for (int i = 0; i < sampleCount; ++i) {
			HistogramSample result = ComputeSample(arena, rng, i);
			if (result.radiance > FLT_EPSILON) {
				int block = 0;
			}
			camera->film->AddSample(samples[i], PaintData(Ls[i], result));
		}

		// Free _MemoryArena_ memory from computing image sample values
		arena.FreeAll();
	}

	// Clean up after _SamplerRendererTask_ is done with its image region
	delete sampler;
	delete[] samples;
	delete[] rays;
	delete[] Ls;
	delete[] Ts;
	delete[] isects;
	reporter.Update();
}


HistogramSample ToFDirectTask::ComputeSample(MemoryArena &arena, RNG &rng, int idx) {
	// Find camera ray for _sample[i]_
	float rayWeight = camera->GenerateRayDifferential(samples[idx], &rays[idx]);
	rays[idx].ScaleDifferentials(1.f / sqrtf(sampler->samplesPerPixel));

	// Evaluate radiance along camera ray
	if (rayWeight > 0.f)
		Ls[idx] = rayWeight * renderer->Li(scene, rays[idx], &samples[idx], rng,
		arena, &isects[idx], &Ts[idx]).L;
	else {
		Ls[idx] = 0.f;
		Ts[idx] = 1.f;
	}

	float LsAvg = Ls[idx].Average(camera->lambdaMin, camera->lambdaMax);
	ValidateL(Ls[idx], LsAvg);

	// Add ground truth sample to ToF data array
	float distance = (isects[idx].dg.p - rays[idx].o).Length();
	float radiance = LsAvg;

	// Apply uniform sampling
	distance /= sampler->samplesPerPixel;
	radiance /= sampler->samplesPerPixel;

	HistogramSample sample(distance, radiance, samples[idx]);
	return sample;
}



void ToFDirectTask::ValidateL(Spectrum &Ls, float LsAvg) {
	// Issue warning if unexpected radiance value returned
	if (Ls.HasNaNs()) {
		Error("Not-a-number radiance value returned "
			"for image sample.  Setting to black.");
		Ls = Spectrum(0.f);
	}
	else if (LsAvg < -1e-5) {
		Error("Negative luminance value, %f, returned "
			"for image sample.  Setting to black.", LsAvg);
		Ls = Spectrum(0.f);
	}
	else if (isinf(LsAvg)) {
		Error("Infinite luminance value returned "
			"for image sample.  Setting to black.");
		Ls = Spectrum(0.f);
	}
}